namespace MoreLinq;

using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Represents a union over list types implementing either
/// <see cref="IList{T}"/> or <see cref="IReadOnlyList{T}"/>, allowing
/// both to be treated the same.
/// </summary>

readonly struct ListLike<T>
{
    readonly IList<T>? rw;
    readonly IReadOnlyList<T>? ro;

    public ListLike(IList<T> list)
    {
        this.rw = list ?? throw new ArgumentNullException(nameof(list));
        this.ro = null;
    }

    public ListLike(IReadOnlyList<T> list)
    {
        this.rw = null;
        this.ro = list ?? throw new ArgumentNullException(nameof(list));
    }

    public int Count => this.rw?.Count ?? this.ro?.Count ?? 0;

    public T this[int index] => this.rw is { } rw ? rw[index]
        : this.ro is { } rx ? rx[index]
        : throw new ArgumentOutOfRangeException(nameof(index));
}

static class ListLike
{
    public static ListLike<T> AsListLike<T>(this List<T> list) => new((IList<T>)list);

    public static ListLike<T> ToListLike<T>(this IEnumerable<T> source)
        => source.TryAsListLike() ?? source.ToList().AsListLike();

    public static ListLike<T>? TryAsListLike<T>(this IEnumerable<T> source) =>
        source switch
        {
            null => throw new ArgumentNullException(nameof(source)),
            IList<T> list => new(list),
            IReadOnlyList<T> list => new(list),
            _ => null
        };
}
